home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 June / MacFormat 25.iso / Shareware City / Developers / OutOfPhase1.1 Source / OutOfPhase Folder / PlayTrackInfoThang.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-01-06  |  30.8 KB  |  908 lines  |  [TEXT/KAHL]

  1. /* PlayTrackInfoThang.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    Out Of Phase:  Digital Music Synthesis on General Purpose Computers    */
  5. /*    Copyright (C) 1994  Thomas R. Lawrence                                 */
  6. /*                                                                           */
  7. /*    This program is free software; you can redistribute it and/or modify   */
  8. /*    it under the terms of the GNU General Public License as published by   */
  9. /*    the Free Software Foundation; either version 2 of the License, or      */
  10. /*    (at your option) any later version.                                    */
  11. /*                                                                           */
  12. /*    This program is distributed in the hope that it will be useful,        */
  13. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  14. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */
  15. /*    GNU General Public License for more details.                           */
  16. /*                                                                           */
  17. /*    You should have received a copy of the GNU General Public License      */
  18. /*    along with this program; if not, write to the Free Software            */
  19. /*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
  20. /*                                                                           */
  21. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  22. /*                                                                           */
  23. /*****************************************************************************/
  24.  
  25. #include "MiscInfo.h"
  26. #include "Audit.h"
  27. #include "Debug.h"
  28. #include "Definitions.h"
  29.  
  30. #include "PlayTrackInfoThang.h"
  31. #include "Array.h"
  32. #include "Memory.h"
  33. #include "OscBankPlayer.h"
  34. #include "TrackObject.h"
  35. #include "IncrementalParameterUpdator.h"
  36. #include "Fractions.h"
  37. #include "FrameObject.h"
  38. #include "DeterminedNoteStructure.h"
  39. #include "NoteObject.h"
  40. #include "ErrorDaemon.h"
  41. #include "TrackEffectGenerator.h"
  42. #include "InstrumentStructure.h"
  43.  
  44.  
  45. typedef struct FrozenNoteConsCell
  46.     {
  47.         /* list link */
  48.         struct FrozenNoteConsCell*    Next;
  49.  
  50.         /* good information to be used for tied notes */
  51.         FrozenNoteRec*                        FrozenNote;
  52.  
  53.         /* tie target for tied notes, NIL if there is no tie */
  54.         struct NoteObjectRec*            TieTarget;
  55.  
  56.         /* when this note should take over (in absolute envelope ticks) */
  57.         long                                            ContinuationTime;
  58.     } FrozenNoteConsCell;
  59.  
  60.  
  61. typedef struct OscBankConsCell
  62.     {
  63.         /* link for listing */
  64.         struct OscBankConsCell*        Next;
  65.         struct OscBankConsCell*        Previous;
  66.  
  67.         /* the oscillator bank */
  68.         OscStateBankRec*                    OscBank;
  69.  
  70.         /* still active flag */
  71.         MyBoolean                                    StillActive;
  72.  
  73.         /* tie target for tied notes, NIL if there is no tie */
  74.         struct NoteObjectRec*            TieTarget;
  75.  
  76.         /* this is the start time of the note, used for ordering the scanning gap list */
  77.         long                                            StartTime;
  78.  
  79.         /* this is an ordered list of frozen notes ready for tie continuation */
  80.         /* this is in sorted ascending order of TieContinuationList->ContinuationTime */
  81.         FrozenNoteConsCell*                TieContinuationList;
  82.     } OscBankConsCell;
  83.  
  84.  
  85. struct PlayTrackInfoRec
  86.     {
  87.         /* frame source */
  88.         TrackObjectRec*                    TrackObject;
  89.         /* total number of frames in the track object */
  90.         long                                        TotalNumberOfFrames;
  91.         /* index into frame array. */
  92.         long                                        FrameArrayIndex;
  93.         /* number of cycles until the next frame should be processed.  this is in */
  94.         /* units of duration update cycles.  when this runs out, then another frame */
  95.         /* should be processed. */
  96.         long                                        NextFrameCountDown;
  97.  
  98.         /* list of notes that have been scheduled but haven't been executed yet. */
  99.         /* it contains objects of type OscBankConsCell. */
  100.         OscBankConsCell*                ScanningGapListHead; /* of OscBankConsCell's */
  101.         OscBankConsCell*                ScanningGapListTail; /* of OscBankConsCell's */
  102.         /* this is the current duration update index for the scanning gap list */
  103.         long                                        ExecutionIndex;
  104.         /* this is the index of the first element in the scanning gap list.  when */
  105.         /* the execution index hits or exceeds this value, the first osc bank is popped */
  106.         /* from the scanning gap list and executed.  it is only valid when */
  107.         /* ScanningGapLength is greater than zero (i.e. ScanningGapList is non-empty) */
  108.  
  109.         /* this object keeps track of the current value of all parameters, updates them */
  110.         /* as time passes, and evaluates commands passed in from here.  the state of */
  111.         /* this object reflects the state at the front of scanning gap, since notes */
  112.         /* are frozen immediately upon entering scanning gap. */
  113.         IncrParamUpdateRec*            ParameterController;
  114.  
  115.         /* this is the template used for creating oscillator banks */
  116.         OscBankTemplateRec*            OscillatorBankTemplate;
  117.  
  118.         /* this is a list of all currently executing oscillator banks.  there is one */
  119.         /* entry for each note that is currently being played. */
  120.         OscBankConsCell*                ExecutingOscillatorBanks;
  121.  
  122.         /* playback control parameters */
  123.         float                                        EnvelopeUpdateRate;
  124.         float                                        OverallVolumeScaling;
  125.  
  126.         /* area for generating samples into, and applying track effects */
  127.         largefixedsigned*                LocalSampleArray;
  128.         long                                        LocalSampleArrayLength;
  129.  
  130.         /* track effects */
  131.         TrackEffectGenRec*            EffectGenerator;
  132.  
  133.         /* score effects.  we don't actually apply this, but we have it so that */
  134.         /* we can send it commands.  it is a reference to a shared object. */
  135.         TrackEffectGenRec*            ScoreEffectGenerator;
  136.  
  137.         /* flag remembering if we are using stereo */
  138.         MyBoolean                                UsingStereo;
  139.     };
  140.  
  141.  
  142. static OscBankConsCell*                    OscBankConsCellFreeList = NIL;
  143. struct FrozenNoteConsCell*            FrozenNoteConsCellFreeList = NIL;
  144.  
  145.  
  146. /* dispose of cached track data structures */
  147. void                                FlushPlayTrackInfo(void)
  148.     {
  149.         while (OscBankConsCellFreeList != NIL)
  150.             {
  151.                 OscBankConsCell*        Temp;
  152.  
  153.                 Temp = OscBankConsCellFreeList;
  154.                 OscBankConsCellFreeList = OscBankConsCellFreeList->Next;
  155.                 ReleasePtr((char*)Temp);
  156.             }
  157.  
  158.         while (FrozenNoteConsCellFreeList != NIL)
  159.             {
  160.                 FrozenNoteConsCell*    Temp;
  161.  
  162.                 Temp = FrozenNoteConsCellFreeList;
  163.                 FrozenNoteConsCellFreeList = FrozenNoteConsCellFreeList->Next;
  164.                 ReleasePtr((char*)Temp);
  165.             }
  166.     }
  167.  
  168.  
  169. /* create a new track play info thing and set a bunch of parameters.  this also */
  170. /* builds the internal representations for instruments & oscillators for this track. */
  171. PlayTrackInfoRec*        NewPlayTrackInfo(struct TrackObjectRec* TheTrack,
  172.                                             struct InstrumentRec* InstrumentSpecification,
  173.                                             MyBoolean StereoFlag, LargeBCDType OverallVolumeScalingReciprocal,
  174.                                             long SamplingRate, float EnvelopeRate, MyBoolean TimeInterp,
  175.                                             MyBoolean WaveInterp, struct TempoControlRec* TempoControl,
  176.                                             long ScanningGapWidthInEnvelopeTicks,
  177.                                             struct ErrorDaemonRec* ErrorDaemon,
  178.                                             struct MainWindowRec* MainWindow,
  179.                                             struct TrackEffectGenRec* ScoreEffectGenerator)
  180.     {
  181.         PlayTrackInfoRec*    TrackInfo;
  182.  
  183.         CheckPtrExistence(TheTrack);
  184.         CheckPtrExistence(InstrumentSpecification);
  185.         CheckPtrExistence(TempoControl);
  186.         CheckPtrExistence(ErrorDaemon);
  187.         CheckPtrExistence(ScoreEffectGenerator);
  188.         CheckPtrExistence(MainWindow);
  189.  
  190.         TrackInfo = (PlayTrackInfoRec*)AllocPtrCanFail(sizeof(PlayTrackInfoRec),
  191.             "PlayTrackInfoRec");
  192.         if (TrackInfo == NIL)
  193.             {
  194.              FailurePoint1:
  195.                 return NIL;
  196.             }
  197.  
  198.         /* score effect generator */
  199.         TrackInfo->ScoreEffectGenerator = ScoreEffectGenerator;
  200.  
  201.         /* frame source */
  202.         TrackInfo->TrackObject = TheTrack;
  203.  
  204.         /* total number of frames in the track object */
  205.         TrackInfo->TotalNumberOfFrames = TrackObjectGetNumFrames(TheTrack);
  206.  
  207.         /* index into frame array. */
  208.         TrackInfo->FrameArrayIndex = 0;
  209.  
  210.         /* number of cycles until the next frame should be processed.  this is in */
  211.         /* units of duration update cycles.  when this runs out, then another frame */
  212.         /* should be processed. */
  213.         TrackInfo->NextFrameCountDown = 0;
  214.  
  215.         /* list of notes that have been scheduled but haven't been executed yet. */
  216.         /* it contains objects of type OscStateBankRec. */
  217.         TrackInfo->ScanningGapListHead = NIL;
  218.         TrackInfo->ScanningGapListTail = NIL;
  219.  
  220.         /* this is the current envelope update index for removing things from the */
  221.         /* scanning gap list (i.e. the back edge of the scanning gap) */
  222.         /* by setting this negative, we cause the scanning gap to open. */
  223.         TrackInfo->ExecutionIndex = - ScanningGapWidthInEnvelopeTicks;
  224.  
  225.         /* this object keeps track of the current value of all parameters, updates them */
  226.         /* as time passes, and evaluates commands passed in from here. */
  227.         TrackInfo->ParameterController = NewInitializedParamUpdator(TheTrack,TempoControl);
  228.         if (TrackInfo->ParameterController == NIL)
  229.             {
  230.              FailurePoint2:
  231.                 ReleasePtr((char*)TrackInfo);
  232.                 goto FailurePoint1;
  233.             }
  234.  
  235.         /* this is the template used for creating oscillator banks */
  236.         TrackInfo->OscillatorBankTemplate = NewOscBankTemplate(InstrumentSpecification,
  237.             StereoFlag,OverallVolumeScalingReciprocal,SamplingRate,EnvelopeRate,
  238.             TimeInterp,WaveInterp,TrackInfo->ParameterController,ErrorDaemon);
  239.         if (TrackInfo->OscillatorBankTemplate == NIL)
  240.             {
  241.              FailurePoint3:
  242.                 DisposeParamUpdator(TrackInfo->ParameterController);
  243.                 goto FailurePoint2;
  244.             }
  245.  
  246.         /* allocate new sample array thing */
  247.         TrackInfo->LocalSampleArrayLength = 1000;
  248.         TrackInfo->LocalSampleArray = (largefixedsigned*)AllocPtrCanFail(
  249.             TrackInfo->LocalSampleArrayLength * sizeof(largefixedsigned),"LocalSampleArray");
  250.         if (TrackInfo->LocalSampleArray == NIL)
  251.             {
  252.              FailurePoint4:
  253.                 DisposeOscBankTemplate(TrackInfo->OscillatorBankTemplate);
  254.                 goto FailurePoint3;
  255.             }
  256.  
  257.         /* set up track effects */
  258.         TrackInfo->EffectGenerator = NewTrackEffectGenerator(GetInstrumentEffectSpecList(
  259.             InstrumentSpecification),SamplingRate,StereoFlag,LargeBCD2Single(
  260.             OverallVolumeScalingReciprocal),MainWindow,ScanningGapWidthInEnvelopeTicks);
  261.         if (TrackInfo->EffectGenerator == NIL)
  262.             {
  263.              FailurePoint5:
  264.                 ReleasePtr((char*)TrackInfo->LocalSampleArray);
  265.                 goto FailurePoint4;
  266.             }
  267.  
  268.         /* remember stereo status */
  269.         TrackInfo->UsingStereo = StereoFlag;
  270.  
  271.         /* this is a list of all currently executing oscillator banks.  there is one */
  272.         /* entry for each note that is currently being played. */
  273.         TrackInfo->ExecutingOscillatorBanks = NIL;
  274.  
  275.         /* playback control parameters */
  276.         TrackInfo->EnvelopeUpdateRate = EnvelopeRate;
  277.         TrackInfo->OverallVolumeScaling = (float)1
  278.             / LargeBCD2Double(OverallVolumeScalingReciprocal);
  279.  
  280.         return TrackInfo;
  281.     }
  282.  
  283.  
  284. static void                    DisposeOscBankList(OscBankConsCell* List)
  285.     {
  286.         while (List != NIL)
  287.             {
  288.                 OscBankConsCell*    Temp;
  289.  
  290.                 /* delink */
  291.                 Temp = List;
  292.                 List = List->Next;
  293.                 /* dispose members */
  294.                 DisposeOscStateBank(Temp->OscBank);
  295.                 while (Temp->TieContinuationList != NIL)
  296.                     {
  297.                         FrozenNoteConsCell*        ColdTemp;
  298.  
  299.                         ColdTemp = Temp->TieContinuationList;
  300.                         Temp->TieContinuationList = Temp->TieContinuationList->Next;
  301.                         DisposeFrozenNote(ColdTemp->FrozenNote);
  302.                         ColdTemp->Next = FrozenNoteConsCellFreeList;
  303.                         FrozenNoteConsCellFreeList = ColdTemp;
  304.                     }
  305.                 /* stick on free list */
  306.                 Temp->Next = OscBankConsCellFreeList;
  307.                 EXECUTE(Temp->Previous = (OscBankConsCell*)0x81818181;)
  308.                 OscBankConsCellFreeList = Temp;
  309.             }
  310.     }
  311.  
  312.  
  313. /* dump a track play info thing and all the stuff in it */
  314. void                                DisposePlayTrackInfo(PlayTrackInfoRec* TrackInfo)
  315.     {
  316.         CheckPtrExistence(TrackInfo);
  317.         DisposeTrackEffectGenerator(TrackInfo->EffectGenerator);
  318.         ReleasePtr((char*)TrackInfo->LocalSampleArray);
  319.         DisposeOscBankList(TrackInfo->ScanningGapListHead);
  320.         DisposeOscBankList(TrackInfo->ExecutingOscillatorBanks);
  321.         DisposeOscBankTemplate(TrackInfo->OscillatorBankTemplate);
  322.         DisposeParamUpdator(TrackInfo->ParameterController);
  323.         ReleasePtr((char*)TrackInfo);
  324.     }
  325.  
  326.  
  327. /* cue track forward to the specified point.  returns False if it fails */
  328. MyBoolean                        CuePlayTrackInfoToPoint(PlayTrackInfoRec* TrackInfo,
  329.                                             struct FractionRec* StartTime)
  330.     {
  331.         long                            AdvancementCounter;
  332.  
  333.         CheckPtrExistence(TrackInfo);
  334.  
  335.         /* convert whole-note fraction into duration ticks */
  336.         ERROR(StartTime->Denominator != DURATIONUPDATECLOCKRESOLUTION,PRERR(AllowResume,
  337.             "CuePlayTrackInfoToPoint:  start time denominator has bad value"));
  338.         AdvancementCounter = StartTime->Integer * StartTime->Denominator
  339.             + StartTime->Fraction;
  340.  
  341.         /* search for the proper point to start playing */
  342.         while ((AdvancementCounter > 0)
  343.             && (TrackInfo->FrameArrayIndex < TrackInfo->TotalNumberOfFrames))
  344.             {
  345.                 FrameObjectRec*        Frame; /* I've been framed! */
  346.                 FractionRec                Duration;
  347.                 long                            TicksInFrame;
  348.  
  349.                 /* get the frame */
  350.                 Frame = TrackObjectGetFrame(TrackInfo->TrackObject,TrackInfo->FrameArrayIndex);
  351.                 CheckPtrExistence(Frame);
  352.                 /* how long is it? */
  353.                 DurationOfFrame(Frame,&Duration);
  354.                 ERROR(Duration.Denominator != DURATIONUPDATECLOCKRESOLUTION,PRERR(AllowResume,
  355.                     "CuePlayTrackInfoToPoint:  start time denominator has odd value"));
  356.                 TicksInFrame = Duration.Integer * Duration.Denominator + Duration.Fraction;
  357.                 /* decrement our lead-in counter */
  358.                 AdvancementCounter -= TicksInFrame;
  359.                 /* advance our pointer */
  360.                 TrackInfo->FrameArrayIndex += 1;
  361.             }
  362.  
  363.         /* AdvancementCounter is either 0 or negative.  if it's negative, then we */
  364.         /* need to note that we must delay some before getting the next note */
  365.         TrackInfo->NextFrameCountDown = - AdvancementCounter;
  366.  
  367.         return True;
  368.     }
  369.  
  370.  
  371. /* check to see if the track has finished and can be dropped. */
  372. MyBoolean                        PlayTrackIsItStillActive(PlayTrackInfoRec* TrackInfo)
  373.     {
  374.         OscBankConsCell*    OscBankScan;
  375.  
  376.         CheckPtrExistence(TrackInfo);
  377.  
  378.         /* in order to still be active, the following conditions must be satisfied: */
  379.         /*  - there are still active oscillators. */
  380.         /*  - there are still oscillators in the scanning gap list */
  381.         /*  - there are still notes which haven't been scanned yet */
  382.  
  383.         if (TrackInfo->FrameArrayIndex < TrackInfo->TotalNumberOfFrames)
  384.             {
  385.                 return True;
  386.             }
  387.  
  388.         if (TrackInfo->ScanningGapListHead != NIL)
  389.             {
  390.                 return True;
  391.             }
  392.  
  393.         OscBankScan = TrackInfo->ExecutingOscillatorBanks;
  394.         while (OscBankScan != NIL)
  395.             {
  396.                 if (OscBankScan->StillActive)
  397.                     {
  398.                         return True;
  399.                     }
  400.                 OscBankScan = OscBankScan->Next;
  401.             }
  402.  
  403.         return False;
  404.     }
  405.  
  406.  
  407. /* auxilliary routine which searches a list for a tie source and installs the note */
  408. /* in the list if necessary.  True is returned if it is installed. */
  409. static MyBoolean        SearchForTieSource(OscBankConsCell* OscBankScan,
  410.                                             struct NoteObjectRec* Note, long ScanningGapFrontInEnvelopeTicks,
  411.                                             PlayTrackInfoRec* TrackInfo, float EnvelopeTicksPerDurationTick)
  412.     {
  413.         while (OscBankScan != NIL)
  414.             {
  415.                 FrozenNoteConsCell*        PlaceToPut;
  416.                 long                                    StartAdjust;
  417.  
  418.                 PlaceToPut = NIL;
  419.                 if (OscBankScan->TieTarget == Note)
  420.                     {
  421.                         /* found it */
  422.                      CreateTieContinuationPoint:
  423.                         if (FrozenNoteConsCellFreeList != NIL)
  424.                             {
  425.                                 PlaceToPut = FrozenNoteConsCellFreeList;
  426.                                 FrozenNoteConsCellFreeList = FrozenNoteConsCellFreeList->Next;
  427.                             }
  428.                          else
  429.                             {
  430.                                 PlaceToPut = (FrozenNoteConsCell*)AllocPtrCanFail(
  431.                                     sizeof(FrozenNoteConsCell),"FrozenNoteConsCell");
  432.                                 if (PlaceToPut == NIL)
  433.                                     {
  434.                                         return False;
  435.                                     }
  436.                             }
  437.                     }
  438.                  else
  439.                     {
  440.                         FrozenNoteConsCell*        TargScan;
  441.                         FrozenNoteConsCell*        TargLag;
  442.  
  443.                         /* search tie target things */
  444.                         TargScan = OscBankScan->TieContinuationList;
  445.                         TargLag = NIL;
  446.                         while (TargScan != NIL)
  447.                             {
  448.                                 if (TargScan->TieTarget == Note)
  449.                                     {
  450.                                         /* found one */
  451.                                         goto CreateTieContinuationPoint;
  452.                                     }
  453.                                 TargLag = TargScan;
  454.                                 TargScan = TargScan->Next;
  455.                             }
  456.                     }
  457.                 if (PlaceToPut != NIL)
  458.                     {
  459.                         FrozenNoteConsCell*        InsertScan;
  460.                         FrozenNoteConsCell*        InsertLag;
  461.  
  462.                         /* fill in the fields */
  463.                         PlaceToPut->FrozenNote = FixNoteParameters(
  464.                             TrackInfo->ParameterController,Note,&StartAdjust,
  465.                             TrackInfo->OverallVolumeScaling,EnvelopeTicksPerDurationTick);
  466.                         if (PlaceToPut->FrozenNote == NIL)
  467.                             {
  468.                                 PlaceToPut->Next = FrozenNoteConsCellFreeList;
  469.                                 FrozenNoteConsCellFreeList = PlaceToPut;
  470.                                 return False;
  471.                             }
  472.                         PlaceToPut->ContinuationTime = StartAdjust + ScanningGapFrontInEnvelopeTicks;
  473.                         PlaceToPut->TieTarget = GetNoteTieTarget(Note);
  474.                         /* insert it into the proper place */
  475.                         InsertScan = OscBankScan->TieContinuationList;
  476.                         InsertLag = NIL;
  477.                         while ((InsertScan != NIL) && (InsertScan->ContinuationTime
  478.                             <= PlaceToPut->ContinuationTime))
  479.                             {
  480.                                 InsertLag = InsertScan;
  481.                                 InsertScan = InsertScan->Next;
  482.                             }
  483.                         PlaceToPut->Next = InsertScan;
  484.                         if (InsertLag == NIL)
  485.                             {
  486.                                 OscBankScan->TieContinuationList = PlaceToPut;
  487.                             }
  488.                          else
  489.                             {
  490.                                 InsertLag->Next = PlaceToPut;
  491.                             }
  492.                         /* we found it! */
  493.                         return True;
  494.                     }
  495.                 OscBankScan = OscBankScan->Next;
  496.             }
  497.         return False;
  498.     }
  499.  
  500.  
  501. /* perform one envelope clock cycle update.  if UpdateEnvelopes is true, then */
  502. /* wave data should be generated and envelopes should be updated, otherwise only */
  503. /* note scheduling should be performed. */
  504. MyBoolean                        PlayTrackUpdate(PlayTrackInfoRec* TrackInfo,
  505.                                             MyBoolean UpdateEnvelopes, long NumDurationTicks,
  506.                                             long NumFrames, largefixedsigned* OutputData,
  507.                                             float EnvelopeTicksPerDurationTick,
  508.                                             long ScanningGapFrontInEnvelopeTicks)
  509.     {
  510.         OscBankConsCell*    OscBankScan;
  511.         OscBankConsCell*    OscBankLag;
  512.  
  513.         CheckPtrExistence(TrackInfo);
  514.  
  515.         /* schedule any notes out of the track list into the scanning gap */
  516.         while ((TrackInfo->NextFrameCountDown <= 0)
  517.             && (TrackInfo->FrameArrayIndex < TrackInfo->TotalNumberOfFrames))
  518.             {
  519.                 FrameObjectRec*        Frame;
  520.  
  521.                 /* schedule a frame */
  522.                 Frame = TrackObjectGetFrame(TrackInfo->TrackObject,TrackInfo->FrameArrayIndex);
  523.                 CheckPtrExistence(Frame);
  524.                 TrackInfo->FrameArrayIndex += 1;
  525.                 if (IsThisACommandFrame(Frame))
  526.                     {
  527.                         NoteObjectRec*        Command;
  528.  
  529.                         /* it's a command */
  530.                         Command = GetNoteFromFrame(Frame,0);
  531.                         /* if it's an effect command, then send it to the effect processor */
  532.                         /* otherwise we handle it */
  533.                         switch (GetCommandOpcode(Command))
  534.                             {
  535.                                 default:
  536.                                     ExecuteParamCommandFrame(TrackInfo->ParameterController,Command);
  537.                                     break;
  538.                                 case eCmdSetEffectParam1:
  539.                                 case eCmdIncEffectParam1:
  540.                                 case eCmdSweepEffectParamAbs1:
  541.                                 case eCmdSweepEffectParamRel1:
  542.                                 case eCmdSetEffectParam2:
  543.                                 case eCmdIncEffectParam2:
  544.                                 case eCmdSweepEffectParamAbs2:
  545.                                 case eCmdSweepEffectParamRel2:
  546.                                 case eCmdSetEffectParam3:
  547.                                 case eCmdIncEffectParam3:
  548.                                 case eCmdSweepEffectParamAbs3:
  549.                                 case eCmdSweepEffectParamRel3:
  550.                                 case eCmdSetEffectParam4:
  551.                                 case eCmdIncEffectParam4:
  552.                                 case eCmdSweepEffectParamAbs4:
  553.                                 case eCmdSweepEffectParamRel4:
  554.                                 case eCmdTrackEffectEnable:
  555.                                     if (!TrackEffectHandleCommand(TrackInfo->EffectGenerator,
  556.                                         Command,ScanningGapFrontInEnvelopeTicks))
  557.                                         {
  558.                                             return False;
  559.                                         }
  560.                                     break;
  561.                                 case eCmdSetScoreEffectParam1:
  562.                                 case eCmdIncScoreEffectParam1:
  563.                                 case eCmdSweepScoreEffectParamAbs1:
  564.                                 case eCmdSweepScoreEffectParamRel1:
  565.                                 case eCmdSetScoreEffectParam2:
  566.                                 case eCmdIncScoreEffectParam2:
  567.                                 case eCmdSweepScoreEffectParamAbs2:
  568.                                 case eCmdSweepScoreEffectParamRel2:
  569.                                 case eCmdSetScoreEffectParam3:
  570.                                 case eCmdIncScoreEffectParam3:
  571.                                 case eCmdSweepScoreEffectParamAbs3:
  572.                                 case eCmdSweepScoreEffectParamRel3:
  573.                                 case eCmdSetScoreEffectParam4:
  574.                                 case eCmdIncScoreEffectParam4:
  575.                                 case eCmdSweepScoreEffectParamAbs4:
  576.                                 case eCmdSweepScoreEffectParamRel4:
  577.                                     if (!TrackEffectHandleCommand(TrackInfo->ScoreEffectGenerator,
  578.                                         Command,ScanningGapFrontInEnvelopeTicks))
  579.                                         {
  580.                                             return False;
  581.                                         }
  582.                                     break;
  583.                             }
  584.                     }
  585.                  else
  586.                     {
  587.                         long                            FrameLimit;
  588.                         long                            FrameScan;
  589.                         FractionRec                FrameDuration;
  590.  
  591.                         /* increment the frame counter */
  592.                         DurationOfFrame(Frame,&FrameDuration);
  593.                         ERROR(DURATIONUPDATECLOCKRESOLUTION != FrameDuration.Denominator,
  594.                             PRERR(AllowResume,"PlayTrackUpdate:  strange denominator in frame duration"));
  595.                         TrackInfo->NextFrameCountDown += FrameDuration.Denominator
  596.                             * FrameDuration.Integer + FrameDuration.Fraction;
  597.  
  598.                         /* it's a real note */
  599.                         FrameLimit = NumNotesInFrame(Frame);
  600.                         for (FrameScan = 0; FrameScan < FrameLimit; FrameScan += 1)
  601.                             {
  602.                                 struct NoteObjectRec*    Note;
  603.                                 OscBankConsCell*    NewOscBank;
  604.                                 OscBankConsCell*    LinkingScan;
  605.  
  606.                                 Note = GetNoteFromFrame(Frame,FrameScan);
  607.                                 CheckPtrExistence(Note);
  608.                                 if (GetNoteIsItARest(Note))
  609.                                     {
  610.                                         /* just ignore rests */
  611.                                         goto EndFrameScanPoint; /* this should really be a conditional */
  612.                                     }
  613.  
  614.                                 /* first, scan the oscillator list to see if this is a note that */
  615.                                 /* someone wants to tie to.  if it is, then build a frozen note */
  616.                                 /* structure and add it to the object.  otherwise, build an */
  617.                                 /* oscillator bank and add it to the scanning gap. */
  618.  
  619.                                 /* search all oscillator banks to see if it's a tie target */
  620.                                 if (SearchForTieSource(TrackInfo->ExecutingOscillatorBanks,Note,
  621.                                     ScanningGapFrontInEnvelopeTicks,TrackInfo,
  622.                                     EnvelopeTicksPerDurationTick))
  623.                                     {
  624.                                         goto EndFrameScanPoint;
  625.                                     }
  626.                                 if (SearchForTieSource(TrackInfo->ScanningGapListHead,Note,
  627.                                     ScanningGapFrontInEnvelopeTicks,TrackInfo,
  628.                                     EnvelopeTicksPerDurationTick))
  629.                                     {
  630.                                         goto EndFrameScanPoint;
  631.                                     }
  632.  
  633.                                 /* if we got here, then it's not a tie target */
  634.                                 if (OscBankConsCellFreeList != NIL)
  635.                                     {
  636.                                         NewOscBank = OscBankConsCellFreeList;
  637.                                         OscBankConsCellFreeList = OscBankConsCellFreeList->Next;
  638.                                     }
  639.                                  else
  640.                                     {
  641.                                         NewOscBank = (OscBankConsCell*)AllocPtrCanFail(
  642.                                             sizeof(OscBankConsCell),"OscBankConsCell");
  643.                                         if (NewOscBank == NIL)
  644.                                             {
  645.                                                 return False;
  646.                                             }
  647.                                     }
  648.                                 NewOscBank->OscBank = NewOscBankState(TrackInfo->OscillatorBankTemplate,
  649.                                     &(NewOscBank->StartTime),Note,EnvelopeTicksPerDurationTick);
  650.                                 if (NewOscBank->OscBank == NIL)
  651.                                     {
  652.                                         NewOscBank->Next = OscBankConsCellFreeList;
  653.                                         OscBankConsCellFreeList = NewOscBank;
  654.                                         return False;
  655.                                     }
  656.                                 NewOscBank->StartTime += ScanningGapFrontInEnvelopeTicks; /* fix up start time */
  657.                                 NewOscBank->StillActive = True;
  658.                                 NewOscBank->TieTarget = GetOscStateTieTarget(NewOscBank->OscBank);
  659.                                 NewOscBank->TieContinuationList = NIL;
  660.                                 /* link it in */
  661.                                 LinkingScan = TrackInfo->ScanningGapListTail;
  662.                                 while ((LinkingScan != NIL)
  663.                                     && (LinkingScan->StartTime > NewOscBank->StartTime))
  664.                                     {
  665.                                         LinkingScan = LinkingScan->Previous;
  666.                                     }
  667.                                 if (LinkingScan == NIL)
  668.                                     {
  669.                                         NewOscBank->Previous = NIL;
  670.                                         NewOscBank->Next = TrackInfo->ScanningGapListHead;
  671.                                         if (TrackInfo->ScanningGapListHead != NIL)
  672.                                             {
  673.                                                 TrackInfo->ScanningGapListHead->Previous = NewOscBank;
  674.                                             }
  675.                                         TrackInfo->ScanningGapListHead = NewOscBank;
  676.                                         if (TrackInfo->ScanningGapListTail == NIL)
  677.                                             {
  678.                                                 /* this happens if there were no nodes at all */
  679.                                                 TrackInfo->ScanningGapListTail = NewOscBank;
  680.                                             }
  681.                                     }
  682.                                  else
  683.                                     {
  684.                                         /* insert after Scan */
  685.                                         NewOscBank->Previous = LinkingScan;
  686.                                         NewOscBank->Next = LinkingScan->Next;
  687.                                         LinkingScan->Next = NewOscBank;
  688.                                         if (LinkingScan == TrackInfo->ScanningGapListTail)
  689.                                             {
  690.                                                 /* this happens if Scan was the last element; */
  691.                                                 /* NewNode becomes last element */
  692.                                                 TrackInfo->ScanningGapListTail = NewOscBank;
  693.                                             }
  694.                                     }
  695.  
  696.                              EndFrameScanPoint:
  697.                                 ;
  698.                             }
  699.                     }
  700.             }
  701.  
  702.         /* do timing update */
  703.         TrackInfo->NextFrameCountDown -= NumDurationTicks;
  704.  
  705.         /* update global parameters */
  706.         ExecuteParamUpdate(TrackInfo->ParameterController,NumDurationTicks);
  707.  
  708.         /* generate waveforms, update envelope generators & notes, etc. */
  709.         /* there are 3 stages to stuff */
  710.         /*  1. when UpdateEnvelopes is false, the only action is to queue up notes */
  711.         /*     to be played.  this opens the scanning gap. */
  712.         /*  2. when UpdateEnvelopes is true, but ExecutionIndex is still less than zero, */
  713.         /*     notes that start before the start of the song (due to pre-origin segments */
  714.         /*     or other things) are started and processed. */
  715.         /*  3. Eventually, ExecutionIndex will be >= 0, and the official start of the */
  716.         /*     song will have been passed. */
  717.         if (UpdateEnvelopes)
  718.             {
  719.                 /* make sure buffer is big enough, and zero it out */
  720.                 if (TrackInfo->UsingStereo)
  721.                     {
  722.                         long                            Scan;
  723.  
  724.                         if (NumFrames * 2 > TrackInfo->LocalSampleArrayLength)
  725.                             {
  726.                                 largefixedsigned*        TempArray;
  727.  
  728.                                 TempArray = (largefixedsigned*)ResizePtr(
  729.                                     (char*)TrackInfo->LocalSampleArray,NumFrames * 2
  730.                                     * sizeof(largefixedsigned));
  731.                                 if (TempArray == NIL)
  732.                                     {
  733.                                         return False;
  734.                                     }
  735.                                 TrackInfo->LocalSampleArray = TempArray;
  736.                                 TrackInfo->LocalSampleArrayLength = NumFrames * 2;
  737.                             }
  738.                         /* initialize array */
  739.                         for (Scan = 0; Scan < NumFrames * 2; Scan += 1)
  740.                             {
  741.                                 PRNGCHK(TrackInfo->LocalSampleArray,&(TrackInfo->LocalSampleArray[Scan]),
  742.                                     sizeof((TrackInfo->LocalSampleArray[Scan])));
  743.                                 TrackInfo->LocalSampleArray[Scan] = 0;
  744.                             }
  745.                     }
  746.                  else
  747.                     {
  748.                         long                            Scan;
  749.  
  750.                         if (NumFrames > TrackInfo->LocalSampleArrayLength)
  751.                             {
  752.                                 largefixedsigned*        TempArray;
  753.  
  754.                                 TempArray = (largefixedsigned*)ResizePtr(
  755.                                     (char*)TrackInfo->LocalSampleArray,NumFrames
  756.                                     * sizeof(largefixedsigned));
  757.                                 if (TempArray == NIL)
  758.                                     {
  759.                                         return False;
  760.                                     }
  761.                                 TrackInfo->LocalSampleArray = TempArray;
  762.                                 TrackInfo->LocalSampleArrayLength = NumFrames;
  763.                             }
  764.                         /* initialize array */
  765.                         for (Scan = 0; Scan < NumFrames; Scan += 1)
  766.                             {
  767.                                 PRNGCHK(TrackInfo->LocalSampleArray,&(TrackInfo->LocalSampleArray[Scan]),
  768.                                     sizeof((TrackInfo->LocalSampleArray[Scan])));
  769.                                 TrackInfo->LocalSampleArray[Scan] = 0;
  770.                             }
  771.                     }
  772.  
  773.                 /* see if any ties have to be tripped */
  774.                 OscBankScan = TrackInfo->ExecutingOscillatorBanks;
  775.                 while (OscBankScan != NIL)
  776.                     {
  777.                         while ((OscBankScan->TieContinuationList != NIL)
  778.                             && (OscBankScan->TieContinuationList->ContinuationTime
  779.                             <= TrackInfo->ExecutionIndex))
  780.                             {
  781.                                 FrozenNoteConsCell*        Temp;
  782.  
  783.                                 /* yow, let's do this one! */
  784.                                 /* delink */
  785.                                 Temp = OscBankScan->TieContinuationList;
  786.                                 OscBankScan->TieContinuationList
  787.                                     = OscBankScan->TieContinuationList->Next;
  788.                                 /* execute */
  789.                                 if (!ResetOscBankState(OscBankScan->OscBank,Temp->FrozenNote,
  790.                                     EnvelopeTicksPerDurationTick))
  791.                                     {
  792.                                         return False; /* oh, no! */
  793.                                     }
  794.                                 OscBankScan->TieTarget = Temp->TieTarget;
  795.                                 /* clean up */
  796.                                 DisposeFrozenNote(Temp->FrozenNote);
  797.                                 Temp->Next = FrozenNoteConsCellFreeList;
  798.                                 FrozenNoteConsCellFreeList = Temp;
  799.                             }
  800.                         OscBankScan = OscBankScan->Next;
  801.                     }
  802.  
  803.                 /* schedule a note from the scanning gap */
  804.                 while ((TrackInfo->ScanningGapListHead != NIL)
  805.                     && (TrackInfo->ScanningGapListHead->StartTime <= TrackInfo->ExecutionIndex))
  806.                     {
  807.                         OscBankConsCell*    NewConsCell;
  808.  
  809.                         /* yup, schedule the oscillator */
  810.                         /* [technically, the start time should never be strictly less than the */
  811.                         /* execution index (only equal), but it can occur if the user specifies */
  812.                         /* a scanning gap that's two narrow.] */
  813.                         NewConsCell = TrackInfo->ScanningGapListHead;
  814.                         TrackInfo->ScanningGapListHead = TrackInfo->ScanningGapListHead->Next;
  815.                         if (TrackInfo->ScanningGapListHead != NIL)
  816.                             {
  817.                                 TrackInfo->ScanningGapListHead->Previous = NIL;
  818.                             }
  819.                          else
  820.                             {
  821.                                 TrackInfo->ScanningGapListTail = NIL;
  822.                             }
  823.                         /* link it in */
  824.                         NewConsCell->Next = TrackInfo->ExecutingOscillatorBanks;
  825.                         TrackInfo->ExecutingOscillatorBanks = NewConsCell;
  826.                         EXECUTE(NewConsCell->Previous = (OscBankConsCell*)0x81818181;)
  827.                     }
  828.  
  829.                 /* let track effect generator schedule commands */
  830.                 TrackEffectProcessQueuedCommands(TrackInfo->EffectGenerator);
  831.  
  832.                 /* increment our scanning gap back edge clock, after scheduling commands */
  833.                 /* (this way, commands are scheduled on the beginning of the clock they */
  834.                 /* should occur on). */
  835.                 TrackInfo->ExecutionIndex += 1;
  836.  
  837.                 /* wave generator and envelope update loop */
  838.                 OscBankScan = TrackInfo->ExecutingOscillatorBanks;
  839.                 OscBankLag = NIL;
  840.                 while (OscBankScan != NIL)
  841.                     {
  842.                         OscBankScan->StillActive = !UpdateOscStateBank(OscBankScan->OscBank,
  843.                             NumFrames,TrackInfo->LocalSampleArray);
  844.                         if (!OscBankScan->StillActive && (OscBankScan->TieContinuationList == NIL))
  845.                             {
  846.                                 OscBankConsCell*    Temp;
  847.  
  848.                                 /* not tied to anybody, so kill it */
  849.                                 DisposeOscStateBank(OscBankScan->OscBank);
  850.                                 if (OscBankLag == NIL)
  851.                                     {
  852.                                         TrackInfo->ExecutingOscillatorBanks = OscBankScan->Next;
  853.                                     }
  854.                                  else
  855.                                     {
  856.                                         OscBankLag->Next = OscBankScan->Next;
  857.                                     }
  858.                                 Temp = OscBankScan;
  859.                                 OscBankScan = OscBankScan->Next;
  860.                                 Temp->Next = OscBankConsCellFreeList;
  861.                                 OscBankConsCellFreeList = Temp;
  862.                             }
  863.                          else
  864.                             {
  865.                                 OscBankLag = OscBankScan;
  866.                                 OscBankScan = OscBankScan->Next;
  867.                             }
  868.                     }
  869.  
  870.                 /* apply effects to local array */
  871.                 ApplyTrackEffectGenerator(TrackInfo->EffectGenerator,
  872.                     TrackInfo->LocalSampleArray,NumFrames);
  873.  
  874.                 /* copy data from local array to global array */
  875.                 if (TrackInfo->UsingStereo)
  876.                     {
  877.                         long                            Scan;
  878.  
  879.                         for (Scan = 0; Scan < NumFrames * 2; Scan += 1)
  880.                             {
  881.                                 PRNGCHK(TrackInfo->LocalSampleArray,&(TrackInfo->LocalSampleArray[Scan]),
  882.                                     sizeof((TrackInfo->LocalSampleArray[Scan])));
  883.                                 PRNGCHK(OutputData,&(OutputData[Scan]),sizeof(OutputData[Scan]));
  884.                                 OutputData[Scan] += TrackInfo->LocalSampleArray[Scan];
  885.                             }
  886.                     }
  887.                  else
  888.                     {
  889.                         long                            Scan;
  890.  
  891.                         for (Scan = 0; Scan < NumFrames; Scan += 1)
  892.                             {
  893.                                 PRNGCHK(TrackInfo->LocalSampleArray,&(TrackInfo->LocalSampleArray[Scan]),
  894.                                     sizeof((TrackInfo->LocalSampleArray[Scan])));
  895.                                 PRNGCHK(OutputData,&(OutputData[Scan]),sizeof(OutputData[Scan]));
  896.                                 OutputData[Scan] += TrackInfo->LocalSampleArray[Scan];
  897.                             }
  898.                     }
  899.             }
  900.  
  901.         /* update effects, but only AFTER they have been applied, so that parameters */
  902.         /* come from the leading edge of an envelope period, rather than the */
  903.         /* trailing edge. */
  904.         TrackEffectIncrementDurationTimer(TrackInfo->EffectGenerator,NumDurationTicks);
  905.  
  906.         return True;
  907.     }
  908.